package cs2114.antiFingerMines;

import java.util.Random;

/**
 * // -------------------------------------------------------------------------
 * /** Holds the array with all of the mines.
 *
 * @author Blake Whaley
 * @author Graham McLaughlin (gjm1324)
 * @author William Vuong (wnvuong)
 * @version 2012.29.4
 */
public class MineField
    extends AntiFingerMineAbstract
{

    // Initializes the threads
    MineDistanceOne   one;
    MineDistanceTwo   two;
    MineDistanceThree three;
    MineDistanceFour  four;
    MineDistanceFive  five;


    /**
     * Creates a new Mine field.
     * @param w is the view width
     * @param h is the view height
     * @param mines is the number of mines to be placed
     */
    public MineField(float w, float h, int mines)
    {
        newGame(w, h, mines);
    }

    /**
     * This method returns the float distance to the closest mine using
     * threads
     * @return float is the distance to the closest mine
     */
    public float distanceToClosestMine()
    {
        float closest;
        // Creates 5 threads with the location of the finger and the minefield.
        one = new MineDistanceOne(finger, field);
        two = new MineDistanceTwo(finger, field);
        three = new MineDistanceThree(finger, field);
        four = new MineDistanceFour(finger, field);
        five = new MineDistanceFive(finger, field);

        // Starts all five threads
        one.run();
        two.run();
        three.run();
        four.run();
        five.run();

        // Stops the five threads
        one.stop();
        two.stop();
        three.stop();
        four.stop();
        five.stop();

        // Gets the value for closest at the first fifth of the field
        closest = one.getClosest();

        // These four if statements compare closest to the values from the
        // other threads setting closest to the smallest one each time.
        if (two.getClosest() < closest)
        {
            closest = two.getClosest();
        }

        if (three.getClosest() < closest)
        {
            closest = three.getClosest();
        }

        if (four.getClosest() < closest)
        {
            closest = four.getClosest();
        }

        if (five.getClosest() < closest)
        {
            closest = five.getClosest();
        }

        setChanged();
        notifyObservers();
        return closest;
    }


    /**
     * Randomly places mines in the array.
     */
    public void placeMines()
    {

        int i = 0;
        while (numMines > 0)
        {

            Mine newMine = generateMine();
            field[i] = newMine;

            while (newMine.distanceFromBlast(finger.getXloc(),
            		finger.getYloc()) <= 10)
            {
                newMine = generateMine();
            }

            for (int j = 0; j < field.length - numMines; j++)
            {
                double between = field[j].traversableDistanceBetween(newMine);
                double fingerDist =
                    newMine.traversableDistanceBetween(getFingerLocation());
                if (between < 40 || fingerDist <= 20)
                {

                    newMine = generateMine();
                    j = -1;
                }
            }
            field[i] = newMine;
            i++;
            numMines--;
        }
    }

    /**
     * Returns the width of the view
     * @return float is the width of the view
     */
    /**
     * places mine
     * @return randomly placed mine
     */

    public Mine generateMine()
    {
    	Random rand = new Random();
    	int wmine = rand.nextInt((int)viewWidth);
        int hmine = (int) (rand.nextInt((int) (endOfField -
        		startOfField)) + startOfField);
        int rlimit = rand.nextInt(60) + 20;
        Mine newMine = new Mine(wmine, hmine, rlimit);
    	wmine = rand.nextInt((int)viewWidth);
        rlimit = rand.nextInt(60) + 20;
    	hmine = (int) (rand.nextInt((int) (endOfField -
        		startOfField - rlimit)) + startOfField);
        newMine = new Mine(wmine, hmine, rlimit);

        return newMine;
    }

    /**
     *
     * @return
     */
    public float getWidth()
    {
        return viewWidth;
    }

    /**
     * Returns the height of the view
     * @return float is the height of the view
     */
    public float getHeight()
    {
        return viewHeight;
    }

    /**
     * Returns the number of mines of the minefield
     * @return int is the number of mines of the minefield
     */
    public int size()
    {
        return field.length;
    }

    /**
     * Returns the mine at the given index location
     * @param index is the location of the wanted mine
     * @return Mine is the mine at the index
     */
    public Mine getMine(int index)
    {
        return field[index];
    }

    /**
     * Returns the location of the finger as a location class
     * @return Location is the location of the finger.
     */
    @Override
    public Location getFingerLocation()
    {
        return finger;
    }


    @Override
    public void setFingerLocation(float x, float y)
    {
        finger.setXloc(x);
        finger.setYloc(y);
// this.isGameOver();
        setChanged();
// notifyObservers();
    }


    /**
     * Checks to see if the game is over returning true if it is and
     * false if it isn't
     *
     * @return boolean true if lost false if not
     */
    public boolean isGameOver()
    {
        loseStatus = false;
        if (this.distanceToClosestMine() <= 0)
        {
            loseStatus = true;
        }
        return loseStatus;
    }

    /**
     * This creates a newGame with the given width, height, and number of mines
     * @param w is the float value of the view width
     * @param h is the float value of the view height
     * @param mines is the number of mines to be added
     */
    @Override
    public void newGame(float w, float h, int mines)
    {
        numMines = mines;
        viewWidth = w;
        viewHeight = h;
        startOfField = viewHeight / 10;
        endOfField = viewHeight - 2 * startOfField;
        field = new Mine[numMines];
        finger = new Location(viewWidth / 2, 25);
        // System.out.println(finger.getXloc() + "," + finger.getYloc());
        spaceForMine = (endOfField - startOfField) / 2;
        // The reason why the message kept coming up was because you forgot
        // to reset the gamelost status to false, so everytime we the button
        // made a new game it still saw the boolean as true and thought we lost.
        // The button works now.
        loseStatus = false;
        winStatus = false;
        this.placeMines();

    }

    /**
     * This method makes a new game with setRandom mines instead of place mines
     *
     * @param w
     * @param h
     * @param mines
     */
    public void newGame1(float w, float h, int mines)
    {
        numMines = mines;
        viewWidth = w;
        viewHeight = h;
        startOfField = viewHeight / 10;
        endOfField = viewHeight - 2 * startOfField;
        field = new Mine[numMines];
        finger = new Location(viewWidth / 2, 25);
        spaceForMine = (endOfField - startOfField) / 2;
        // The reason why the message kept coming up was because you forgot
        // to reset the gamelost status to false, so everytime we the button
        // made a new game it still saw the boolean as true and thought we lost.
        // The button works now.
        loseStatus = false;
        winStatus = false;
        this.setRandomMines();

    }


    @Override
    public void setWidth(float w)
    {
        viewWidth = w;

    }


    @Override
    public void setHeight(float h)
    {
        viewHeight = h;

    }


    @Override
    public void setRandomMines()
    {
        Random rand = new Random();

        for (int i = 0; i < numMines; i++)
        {
            field[i] =
                new Mine(
                    rand.nextFloat() * viewWidth,
                    startOfField + i * spaceForMine + rand.nextFloat()
                        * spaceForMine,
                    spaceForMine);
        }

    }


    /**
     * Checks to see if the game is won by seeing if you have crossed the green
     * win zone
     *
     * @return boolean true if won, false if not
     */
    public boolean hasWon()
    {
        winStatus = false;
        if ((finger.getYloc() + 20) > endOfField)
        {
            winStatus = true;
        }
        return winStatus;
    }

    /**
     * Returns the float location of the end of the minefield
     * @return float is the value of the end of the minefield
     */
    public float getEndOfField()
    {
        return endOfField;
    }

    /**
     * This class along with the four other MineDistance classes allows us to
     *  calculate the distance from the finger and all the mines 4 to 5
     *  times faster by extending Thread
     *
     *  Thread allows you run multiple sets code of at one time. In our case
     *  since we have 5 threads. We can do all 5 threads at the same time.
     *  So we are calculating the distance from our finger to five mines at
     *  one time as opposed to one mine at one time. There is a lot less lag.
     */
    private class MineDistanceOne
        extends Thread
    {

        // Initializes the finger, minefield, and closest to be set in the
        // constructor
        Location finger1;
        Mine[]   field1;
        float    closest;


        // ----------------------------------------------------------
        /**
         * Create a new MineDistanceOne object.
         *
         * @param loc
         *            the fingers current location
         * @param mines
         *            the field of mines
         */
        public MineDistanceOne(Location loc, Mine[] mines)
        {
            finger1 = loc;
            field1 = mines;
            // Sets closest initially to be 99999 so that the actual closest
            // distance will be for sure less than it.
            closest = 99999;
        }


        /**
         * This thread only runs on the first fifth of the mine field and also
         * sets the smallest distance between the finger and the mine to closest
         */
        public void run()
        {
            for (int i = 0; i < field1.length / 5; i++)
            {
                if (field1[i].traversableDistanceBetween(finger1) <= closest)
                {
                    closest = field1[i].traversableDistanceBetween(finger1);
                }
            }

        }


        /**
         * This method returns closest so that it can be compared to the closest
         * values returned from the other threads.
         *
         * @return float is the closest mine to the finger in the first fifth of
         *         the minefield
         */
        public float getClosest()
        {
            return closest;
        }

    }

    /**
     * This class along with the four other MineDistance classes allows us to
     *  calculate the distance from the finger and all the mines 4 to 5
     *  times faster by extending Thread
     *
     *  Thread allows you run multiple sets code of at one time. In our case
     *  since we have 5 threads. We can do all 5 threads at the same time.
     *  So we are calculating the distance from our finger to five mines at
     *  one time as opposed to one mine at one time. There is a lot less lag.
     */
    private class MineDistanceTwo
        extends Thread
    {

        // Variables needed for the thread like finger location, minefield,
        // and a float representing the distance to the closest mine
        Location finger1;
        Mine[]   field1;
        float    closest;


        // ----------------------------------------------------------
        /**
         * Create a new MineDistanceOne object.
         *
         * @param loc
         *            the fingers current location
         * @param mines
         *            the field of mines
         */
        public MineDistanceTwo(Location loc, Mine[] mines)
        {
            finger1 = loc;
            field1 = mines;
            closest = 99999;
        }


        /**
         * This thread only runs on the first fifth of the mine field and also
         * sets the smallest distance between the finger and the mine to closest
         */
        public void run()
        {
            for (int i = field1.length / 5; i < 2 * field1.length / 5; i++)
            {
                if (field1[i].traversableDistanceBetween(finger1) <= closest)
                {
                    closest = field1[i].traversableDistanceBetween(finger1);
                }
            }
        }


        /**
         * This method returns closest so that it can be compared to the closest
         * values returned from the other threads.
         *
         * @return float is the closest mine to the finger in the first fifth of
         *         the minefield
         */
        public float getClosest()
        {
            return closest;
        }

    }

    /**
     * This class along with the four other MineDistance classes allows us to
     *  calculate the distance from the finger and all the mines 4 to 5
     *  times faster by extending Thread
     *
     *  Thread allows you run multiple sets code of at one time. In our case
     *  since we have 5 threads. We can do all 5 threads at the same time.
     *  So we are calculating the distance from our finger to five mines at
     *  one time as opposed to one mine at one time. There is a lot less lag.
     */
    private class MineDistanceThree
        extends Thread
    {

        // Variables needed for the thread like finger location, minefield,
        // and a float representing the distance to the closest mine
        Location finger1;
        Mine[]   field1;
        float    closest;


        // ----------------------------------------------------------
        /**
         * Create a new MineDistanceOne object.
         *
         * @param loc
         *            the fingers current location
         * @param mines
         *            the field of mines
         */
        public MineDistanceThree(Location loc, Mine[] mines)
        {
            finger1 = loc;
            field1 = mines;
            closest = 99999;
        }


        /**
         * This thread only runs on the first fifth of the mine field and also
         * sets the smallest distance between the finger and the mine to closest
         */
        public void run()
        {
            for (int i = 2 * field1.length / 5; i < 3 * field1.length / 5; i++)
            {
                if (field1[i].traversableDistanceBetween(finger1) <= closest)
                {
                    closest = field1[i].traversableDistanceBetween(finger1);
                }
            }
        }


        /**
         * This method returns closest so that it can be compared to the closest
         * values returned from the other threads.
         *
         * @return float is the closest mine to the finger in the first fifth of
         *         the minefield
         */
        public float getClosest()
        {
            return closest;
        }

    }

    /**
     * This class along with the four other MineDistance classes allows us to
     *  calculate the distance from the finger and all the mines 4 to 5
     *  times faster by extending Thread
     *
     *  Thread allows you run multiple sets code of at one time. In our case
     *  since we have 5 threads. We can do all 5 threads at the same time.
     *  So we are calculating the distance from our finger to five mines at
     *  one time as opposed to one mine at one time. There is a lot less lag.
     */
    private class MineDistanceFour
        extends Thread
    {

        // Variables needed for the thread like finger location, minefield,
        // and a float representing the distance to the closest mine
        Location finger1;
        Mine[]   field1;
        float    closest;


        // ----------------------------------------------------------
        /**
         * Create a new MineDistanceOne object.
         *
         * @param loc
         *            the fingers current location
         * @param mines
         *            the field of mines
         */
        public MineDistanceFour(Location loc, Mine[] mines)
        {
            finger1 = loc;
            field1 = mines;
            closest = 99999;
        }


        /**
         * This thread only runs on the first fifth of the mine field and also
         * sets the smallest distance between the finger and the mine to closest
         */
        public void run()
        {
            for (int i = 3 * field1.length / 5; i < 4 * field1.length / 5; i++)
            {
                if (field1[i].traversableDistanceBetween(finger1) <= closest)
                {
                    closest = field1[i].traversableDistanceBetween(finger1);
                }
            }
        }


        /**
         * This method returns closest so that it can be compared to the closest
         * values returned from the other threads.
         *
         * @return float is the closest mine to the finger in the first fifth of
         *         the minefield
         */
        public float getClosest()
        {
            return closest;
        }

    }

    /**
     * This class along with the four other MineDistance classes allows us to
     *  calculate the distance from the finger and all the mines 4 to 5
     *  times faster by extending Thread
     *
     *  Thread allows you run multiple sets code of at one time. In our case
     *  since we have 5 threads. We can do all 5 threads at the same time.
     *  So we are calculating the distance from our finger to five mines at
     *  one time as opposed to one mine at one time. There is a lot less lag.
     */
    private class MineDistanceFive extends Thread
    {

        // Variables needed for the thread like finger location, minefield,
        // and a float representing the distance to the closest mine
        Location finger1;
        Mine[]   field1;
        float    closest;


        // ----------------------------------------------------------
        /**
         * Create a new MineDistanceOne object.
         *
         * @param loc
         *            the fingers current location
         * @param mines
         *            the field of mines
         */
        public MineDistanceFive(Location loc, Mine[] mines)
        {
            finger1 = loc;
            field1 = mines;
            closest = 99999;
        }


        /**
         * This thread only runs on the first fifth of the mine field and also
         * sets the smallest distance between the finger and the mine to closest
         */
        public void run()
        {
            for (int i = 4 * field1.length / 5; i < field1.length; i++)
            {
                if (field1[i].traversableDistanceBetween(finger1) <= closest)
                {
                    closest = field1[i].traversableDistanceBetween(finger1);
                }
            }
        }


        /**
         * This method returns closest so that it can be compared to the closest
         * values returned from the other threads.
         *
         * @return float is the closest mine to the finger in the first fifth of
         *         the minefield
         */

        public float getClosest()
        {
            return closest;
        }

    }

}
